/*******************************************************************/
/* @ All Rights Reserved Copyright (C) 2014 broada,Co.,Ltd.        */
/*                                                                 */
/*  NAME      = frssl_config.c                                     */
/*  NOTE      =                                                    */
/*  DATE      = 2016/01/26 by guojq                                */
/*******************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <pthread.h>
#include <pcap/pcap.h>

#include <wolfssl/wolfcrypt/settings.h>
#include <cyassl/sniffer.h>

#include "frcomm.h"
#include "frlog.h"
#include "frsutl.h"
//#include "frame_cfg.h"
#include "frmem.h"
#include "frdssl.h"
#include "frxml_parse.h"
#include "frssl_config.h"
#include "frssl_crypto.h"

#define DSSL_DOMAIN   "domain:"

int g_i_dssl_task_cnt = 0;
char g_arr_dssl_zmq_client[256];

typedef struct __ssl_info {
    int       i_action;
    u_int     ui_ip;
    u_short   us_port;
    char     *p_file;
    char     *p_passwd;
    char     *p_domain;
} FRSSL_info;

FR_STATIC pthread_rwlock_t g_ssl_rwlock;
FR_STATIC Frlist_header g_lst_ssl_info;

/*******************************************************************************
*  FUNC     :  释放链表节点
*  ARGS     :  Frlist_node* node   需要释放的链表节点
*  RTN      :  void
*  NOTE     :
*******************************************************************************/
FR_STATIC void __info_node_free(Frlist_node* node)
{
    FRSSL_info *p_st_info = NULL;

    if (node == NULL || node->data == NULL)
        return;
    p_st_info = (FRSSL_info *)node->data;

    if (p_st_info->p_file)
        frfree(p_st_info->p_file);
    if (p_st_info->p_passwd)
        frfree(p_st_info->p_passwd);
    if (p_st_info->p_domain)
        frfree(p_st_info->p_domain);
    frfree(p_st_info);
    return;
}

FR_STATIC void __ssl_info_free(FRSSL_info **pp_ssl_info)
{
    FRSSL_info *p_ssl_info = NULL;
    if (pp_ssl_info && *pp_ssl_info)
    {
        p_ssl_info = *pp_ssl_info;
        if (p_ssl_info->p_file)
            frfree(p_ssl_info->p_file);
        if (p_ssl_info->p_passwd)
            frfree(p_ssl_info->p_passwd);
        if (p_ssl_info->p_domain)
            frfree(p_ssl_info->p_domain);
        frfree(p_ssl_info);
        *pp_ssl_info = NULL;
    }
}

/*******************************************************************************
*  FUNC     :  解析ssl一行的数据
*              格式：IP 端口 路径 密码
*                例：172.16.0.31 443 test 123456
*  ARGS     :  p_line                   一行xml数据
*           :  pp_ssl_info              ssl信息的结构体
*  RTN      :  FR_SUCC              
*              FR_FAIL              
*  NOTE     :  
*******************************************************************************/
FR_STATIC int __frssl_analyse_xml(char *p_line, FRSSL_info **pp_ssl_info)
{
    FRSSL_info *p_ssl_info = NULL;
    char *p_tmp = NULL, *p_end = NULL;
    char *p_file = NULL, *p_passwd = NULL, *p_passwd_decode = NULL;
    char *p_domain = NULL;
    u_int ui_ip = 0;
    u_short us_port = 0;
    
    if (p_line == NULL)
        return FR_FAIL;
    
    p_tmp = strchr(p_line, ' ');
    if (p_tmp == NULL)
        return FR_FAIL;
    //取得ip
    ui_ip = iptools_parse_ipaddr(p_line);
    
    p_tmp++;
    //取得端口
    us_port = atoi(p_tmp);
    
    p_tmp = strchr(p_tmp, ' ');
    if (p_tmp == NULL)
        return FR_FAIL;
    p_tmp++;
    
    //取得文件名
    p_end = strchr(p_tmp, ' ');
    if (p_end == NULL)
    {
        p_file = frstrdup(p_tmp);
        goto end;
    }
    
    p_file = (char *)frmalloc(p_end - p_tmp + 1);
    if (p_end == NULL)
        return FR_FAIL;
    snprintf(p_file, p_end - p_tmp + 1, "%s", p_tmp);
    
    p_tmp = p_end + 1;
    
    //尝试查找domain
    if (*p_tmp != '\0')
    {
        if (strncasecmp(p_tmp, DSSL_DOMAIN, strlen(DSSL_DOMAIN)) == 0)
        {
            p_domain = frstrdup(p_tmp + strlen(DSSL_DOMAIN));
            if (p_domain == NULL)
                goto err;
            goto end;
        }
        //查看是否还存在域名
        p_end = strchr(p_tmp, ' ');
        if (p_end)
        {
            //取得密钥
            int i_passwd_len = p_end - p_tmp;
            if (i_passwd_len)
            {
                p_passwd = (char *)frmalloc(i_passwd_len + 1);
                if (p_passwd == NULL)
                    goto err;
                strncpy(p_passwd, p_tmp, i_passwd_len);
                p_passwd[i_passwd_len] = '\0';
            }
            //取得域名
            if (strncasecmp(p_end + 1, DSSL_DOMAIN, strlen(DSSL_DOMAIN)) == 0)
            {
                p_domain = frstrdup(p_end + 1 + strlen(DSSL_DOMAIN));
                if (p_domain == NULL)
                    goto err;
            }
        }
        else
        {
            //取得密钥，密钥可以为空
            p_passwd = frstrdup(p_tmp);
            if (p_passwd == NULL)
            {
                frfree(p_file);
                return FR_FAIL;
            }
        }
    }
    
    //密钥为加密字段，做解密后存储起来
    if (p_passwd)
    {
        //把ssl的密码解密
        if (ssl_config_decrypt(p_passwd, &p_passwd_decode) != FR_SUCC)
        {
            frfree(p_file);
            frfree(p_passwd);
            return FR_FAIL;
        }
        frfree(p_passwd);
    }
    
end:
    p_ssl_info = (FRSSL_info *)frmalloc(sizeof(FRSSL_info));
    if (p_ssl_info == NULL)
    {
        frfree(p_file);
        frfree(p_passwd);
        return FR_FAIL;
    }
    memset(p_ssl_info, 0x00, sizeof(FRSSL_info));
    p_ssl_info->ui_ip = ui_ip;
    p_ssl_info->us_port = us_port;
    p_ssl_info->p_file = p_file;
    p_ssl_info->p_passwd = p_passwd_decode;
    p_ssl_info->p_domain = p_domain;
    
    *pp_ssl_info = p_ssl_info;
    
    return FR_SUCC;
err:
    if (p_file)
        frfree(p_file);
    if (p_domain)
        frfree(p_domain);
    if (p_passwd)
        frfree(p_passwd);
    return FR_FAIL;
}

static int __frssl_update_serverinfo(FRSSL_info *p_ssl_info)
{
    char arr_err[PCAP_ERRBUF_SIZE];
    Frlist_node *p_st_new = NULL;
    
    if (p_ssl_info == NULL)
        return FR_FAIL;

    FRLOG_INFO("__frssl_update_serverinfo domain=[%s],ip=%s,port=%d\n"
        , p_ssl_info->p_domain, int_ntoa(p_ssl_info->ui_ip), p_ssl_info->us_port);
    ssl_SetNamedPrivateKey(p_ssl_info->p_domain, int_ntoa(p_ssl_info->ui_ip)
                         , p_ssl_info->us_port, p_ssl_info->p_file
                         , FILETYPE_PEM, p_ssl_info->p_passwd, arr_err);

    p_st_new = frlist_node_create(p_ssl_info);
    if (p_st_new)
        frlist_add_tail(&g_lst_ssl_info, p_st_new);
    else
        FRLOG_ERROR_DETAIL("frlist_node_create failed");

    return FR_SUCC;
}

FR_STATIC int __check_node(Frlist_node* node, const void *p_sniffer_ipinfo)
{
    sniffer_ipinfo *p_st_sniffer = NULL;
    FRSSL_info *p_st_node_ssl = NULL;

    if (node == NULL || p_sniffer_ipinfo == NULL || node->data == NULL)
        return 1;

    p_st_node_ssl = (FRSSL_info *)node->data;
    p_st_sniffer = (sniffer_ipinfo *)p_sniffer_ipinfo;

    if (p_st_node_ssl->ui_ip != p_st_sniffer->i_sip && p_st_node_ssl->ui_ip != p_st_sniffer->i_dip)
        return 1;

    if (p_st_sniffer->i_sport != p_st_node_ssl->us_port && p_st_sniffer->i_dport != p_st_node_ssl->us_port)
        return 1;

    return 0;
}

/*******************************************************************************
*  FUNC     :  查询ssl信息
*  ARGS     :
*  RTN      :  1:找到
*              0:未找到
*  NOTE     :
*******************************************************************************/
int frssl_config_lookup(sniffer_ipinfo *p_ipinfo)
{
    Frlist_node *p_st_find = NULL;

    if (p_ipinfo == NULL)
        return SSL_CONFIG_NOTFIND;

    p_st_find = frlist_find(&g_lst_ssl_info, (void *)p_ipinfo, __check_node);
    if (p_st_find == NULL)
        return SSL_CONFIG_NOTFIND;

    return SSL_CONFIG_FIND;
}

/*******************************************************************************
*  FUNC     :  加载ssl配置到内存
*  ARGS     :  
*  RTN      :  FR_SUCC
*              FR_FAIL
*  NOTE     :
*******************************************************************************/
int frssl_config_load(void)
{
    Frlist_header *p_xml_head = NULL;
    Frlist_node *p_st_node = NULL;
    Frvar_field *p_st_field_tmp = NULL;
    int i_dssl_task_cnt = 0;
    char *p_value = NULL;

    pthread_rwlock_wrlock(&g_ssl_rwlock);
    frlist_remove_all(&g_lst_ssl_info, __info_node_free);

    //取得线程数
    if (frxml_get2doc_int(APM_CFG_FILENAME_SSL, APM_CFG_SSL_XPATH_DSSL, APM_CFG_SSL_TASK_CNT, &i_dssl_task_cnt) != FR_SUCC
        || i_dssl_task_cnt <= 0)
        g_i_dssl_task_cnt = SSL_TASK_CNT_DEFAULT;
    else
        g_i_dssl_task_cnt = i_dssl_task_cnt;
    
    FRLOG_INFO("ssl task thread count=%d", g_i_dssl_task_cnt);
    
    frxml_get2doc_string(APM_CFG_FILENAME_SSL, APM_CFG_SSL_XPATH_DSSL, APM_CFG_SSL_ZMQ_CLIENT, &p_value);
    if (p_value)
    {
        snprintf(g_arr_dssl_zmq_client, sizeof(g_arr_dssl_zmq_client), "%s", p_value);
        frfree(p_value);
    }
    else
    {
        FRLOG_INFO_DETAIL("[%s] key[%s] err", APM_CFG_FILENAME_SSL, APM_CFG_SSL_ZMQ_CLIENT);
        snprintf(g_arr_dssl_zmq_client, sizeof(g_arr_dssl_zmq_client), "tcp://127.0.0.1:5554");//默认5554端口
    }
    
    FRLOG_INFO("ssl zmq client=%s", g_arr_dssl_zmq_client);

    if (frxml_get_string_list(APM_CFG_FILENAME_SSL, APM_CFG_SSL_XPATH_DSSL, APM_CFG_SSL_KEY_RULE, &p_xml_head) != FR_SUCC)
    {
        FRLOG_ERROR_DETAIL("file [%s] error", APM_CFG_FILENAME_SSL);
        pthread_rwlock_unlock(&g_ssl_rwlock);
        return FR_FAIL;
    }

    p_st_node = FRLIST_FIRST_NODE(p_xml_head);
    while(p_st_node)
    {
        p_st_field_tmp = (Frvar_field *)FRLIST_GET_NODE_DATA(p_st_node);
        if (p_st_field_tmp)
        {
            FRSSL_info *p_ssl_info = NULL;

            //解析一行数据取得ssl对应文件名
            if (__frssl_analyse_xml(p_st_field_tmp->field_value.p_value, &p_ssl_info) != FR_SUCC)
                goto err;

            //把数据添加到sniffer中
            if (__frssl_update_serverinfo(p_ssl_info) != FR_SUCC)
                goto err;
        }
        p_st_node = FRLIST_NEXT_NODE(p_st_node);
    }
    pthread_rwlock_unlock(&g_ssl_rwlock);
    frxml_free_list(&p_xml_head);
    return FR_SUCC;
err:
    pthread_rwlock_unlock(&g_ssl_rwlock);
    frxml_free_list(&p_xml_head);
    return FR_FAIL;
}

int frssl_config_init(void)
{
    if (pthread_rwlock_init(&g_ssl_rwlock, NULL) != 0)
    {
        FRLOG_ERROR_DETAIL("pthread_rwlock_init failed");
        return FR_FAIL;
    }
    frlist_header_init(&g_lst_ssl_info);

    return FR_SUCC;
}

void frssl_config_destroy(void)
{
    pthread_rwlock_destroy(&g_ssl_rwlock);
    frlist_remove_all(&g_lst_ssl_info, __info_node_free);
}
